package com.ray.test.runner;

import com.ray.test.annotation.InitObject;
import com.ray.test.annotation.InitValue;
import com.ray.test.annotation.Mock;
import com.ray.test.proxy.MockTestConfig;
import com.ray.test.proxy.ProxyBean;
import com.ray.test.util.PropertyUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author bo shen
 * @Description: mock第一层数据
 * @Class: RAutoSpringJUnit4ClassRunner
 * @Package test.runner
 * @date 2019/9/29 8:45
 * @company <p>杭州传化陆鲸科技有限公司</p>
 * @updateRecord time(修改时间)  author(修改人)   desc(修改内容)
 */@Slf4j
public class RMockAutoSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {

    /***外部提供的bean**/
    private static ThreadLocal<Map<Class<?>,Object>> beans = new ThreadLocal<>();
    /***外部提供的配置**/
    private static ThreadLocal<Map<String,Object>> values = new ThreadLocal<>();

    /***全局配置**/
    private static ThreadLocal<Boolean> mockData = new ThreadLocal<>();
    /***需要代理执行的对象**/
    private static ThreadLocal<Object> tests = new ThreadLocal<>();

    private static ThreadLocal<ApplicationContext> applicationContexts = new ThreadLocal<>();

    private static ThreadLocal<PlatformTransactionManager> platformTransactionManagers = new ThreadLocal<>();

    private static ThreadLocal<TransactionDefinition> transactionDefinitions = new ThreadLocal<>();


    public RMockAutoSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
        ApplicationContext applicationContext = super.getTestContextManager().getTestContext().getApplicationContext();
        applicationContexts.set(applicationContext);
        try {
            //初始化容器
            PlatformTransactionManager platformTransactionManager = applicationContext.getBean("transactionManager", PlatformTransactionManager.class);
            if (ObjectUtils.isEmpty(platformTransactionManager)) {
                platformTransactionManager = applicationContext.getBean(PlatformTransactionManager.class);
            }
            platformTransactionManagers.set(platformTransactionManager);
            TransactionDefinition transactionDefinition = applicationContext.getBean(TransactionDefinition.class);
            transactionDefinitions.set(transactionDefinition);
        }catch (Exception e){
            log.info("进入无事务模式");
        }
        createManager(clazz);
    }

    /***
     * 创建管理器
     * @param clazz
     */
    private void createManager(Class<?> clazz) {
        try {
            //测试对象
            Object test = getTestClass().getJavaClass().newInstance();
            tests.set(test);
            initObject(clazz,test);
            initValue(clazz,test);
            initSpringBean(clazz,test);
        } catch (Exception e) {
            log.info("MOCK容器启动异常");
        }
    }

    private void initSpringBean(Class<?> clazz,Object test) {
        try {
            List<FrameworkField> frameworkFields = getTestClass().getAnnotatedFields(Autowired.class);
            for (FrameworkField frameworkField : frameworkFields) {
                log.info("初始化Autowired属性;{}",frameworkField.getName());
                Object bean = applicationContexts.get().getBean(frameworkField.getType());
                PropertyUtils.witerProperty(test,frameworkField.getName(),bean);
            }
        } catch (Exception e) {
            log.info("初始化属性设置异常", e);
        }
    }

    private void initObject(Class<?> clazz,Object test) {
        try {
            Map<Class<?>,Object> beanServices = new HashMap<>();
            List<FrameworkField> frameworkFields = getTestClass().getAnnotatedFields(InitObject.class);
            for (FrameworkField frameworkField : frameworkFields) {
                beanServices.put(frameworkField.getType(), PropertyUtils.readProperty(test, frameworkField.getName()));
            }
            beans.set(beanServices);
        } catch (Exception e) {
            log.info("初始化属性设置异常", e);
        }
    }

    private void initValue(Class<?> clazz,Object test) {
        try {
            Map<String,Object> valueServices = new HashMap<>();
            List<FrameworkField> frameworkFields = getTestClass().getAnnotatedFields(InitValue.class);
            for (FrameworkField frameworkField : frameworkFields) {
                valueServices.put(frameworkField.getAnnotation(InitValue.class).name(), PropertyUtils.readProperty(test, frameworkField.getName()));
            }
            values.set(valueServices);
        } catch (Exception e) {
            log.info("初始化属性设置异常", e);
        }
    }

    @Override
    public Object createTest() throws Exception {
        Object test = tests.get();
        //初始化属性
        List<FrameworkField> frameworkFields = getTestClass().getAnnotatedFields(Mock.class);
        if (ObjectUtils.isEmpty(frameworkFields)) {
            throw new RuntimeException("没有找到需要mock的类");
        }
        FrameworkField frameworkField = frameworkFields.get(0);
        Object mock = createMockObject(frameworkField);
        PropertyUtils.witerProperty(test,frameworkField.getName(),mock);
        return test;
    }

    /**
     * 创建一个执行的对象
     *
     * @param frameworkField
     * @return
     */
    private Object createMockObject(FrameworkField frameworkField) throws Exception{
        log.info("加载类型为:{}的执行对象",frameworkField.getType());
        //获得单元测试类
        Mock mock = frameworkField.getAnnotation(Mock.class);
        //实例化当前对象
        Object target = mock.implClass().newInstance();
        mockData.set(mock.mackData());
        //设置代理对象属性 数据对象都进行代理
        return setFiledPorxy(target);
    }

    public Object setFiledPorxy(Object filedPorxy) {
       return new ProxyBean(filedPorxy,beans.get(),values.get(),applicationContexts.get()).create(true);
    }

    @Override
    protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) {
        log.info("当前执行方法:{}",frameworkMethod.getName());
        MockTestConfig.set(frameworkMethod,testInstance.getClass());
        return super.withBefores(frameworkMethod, testInstance, statement);
    }
}


